package com.utils;

import java.util.*;

/**
 * @author xiaoly
 *
 */
public class LunarCalendarUtil {


    /**
     * 农历1900-2100的润大小信息表
     *
     * @Array Of Property
     * @return Hex
     */
    private static int[] lunarInfo = {0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,//1900-1909
            0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,//1910-1919
            0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,//1920-1929
            0x06566, 0x0d4a0, 0x0ea50, 0x16a95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,//1930-1939
            0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,//1940-1949
            0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,//1950-1959
            0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,//1960-1969
            0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6,//1970-1979
            0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,//1980-1989
            0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0,//1990-1999
            0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,//2000-2009
            0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,//2010-2019
            0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,//2020-2029
            0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,//2030-2039
            0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,//2040-2049
            /**Add By JJonline@JJonline.Cn**/
            0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0,//2050-2059
            0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4,//2060-2069
            0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0,//2070-2079
            0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160,//2080-2089
            0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252,//2090-2099
            0x0d520};//2100


    /**
     * 公历每个月份的天数普通表
     *
     * @Array Of Property
     * @return Number
     */

    private static int[] solarMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    /**
     * 天干地支之天干速查表
     *
     * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
     * @return Cn string
     */

//    private static String[] Gan = {"\u7532", "\u4e59", "\u4e19", "\u4e01", "\u620a", "\u5df1", "\u5e9a", "\u8f9b", "\u58ec", "\u7678"};
    private static final String[] Gan = {"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};

    /**
     * 天干地支之地支速查表
     *
     * @Array Of Property
     * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
     * @return Cn string
     */
//    private static String[] Zhi = {"\u5b50", "\u4e11", "\u5bc5", "\u536f", "\u8fb0", "\u5df3", "\u5348", "\u672a", "\u7533", "\u9149", "\u620c", "\u4ea5"};
    private static final String[] Zhi = {"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};


    /**
     * 天干地支之地支速查表<=>生肖
     *
     * @Array Of Property
     * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
     * @return Cn string
     */

//    private static String[] Animals = {"\u9f20", "\u725b", "\u864e", "\u5154", "\u9f99", "\u86c7", "\u9a6c", "\u7f8a", "\u7334", "\u9e21", "\u72d7", "\u732a"};
    // Array of Chinese zodiac animals
    private static final String[] Animals = {"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};

    /**
     * 阳历节日
     */
    // 公历节日映射
    private static Map<String, String> festival = new HashMap<String, String>();

    // 静态块，用于初始化节日映射
    static {
        festival.put("1-1", "元旦节");
        festival.put("2-14", "情人节");
        festival.put("5-1", "劳动节");
        festival.put("5-4", "青年节");
        festival.put("6-1", "儿童节");
        festival.put("9-10", "教师节");
        festival.put("10-1", "国庆节");
        festival.put("12-25", "圣诞节");

        festival.put("3-8", "妇女节");
        festival.put("3-12", "植树节");
        festival.put("4-1", "愚人节");
        festival.put("5-12", "护士节");
        festival.put("7-1", "建党节");
        festival.put("8-1", "建军节");
        festival.put("12-24", "平安夜");
    }


    /**
     * 农历节日
     */

    // 农历节日映射
    private static Map<String, String> lFestival = new HashMap<String, String>();

    // 静态块，用于初始化农历节日映射
    static {
        lFestival.put("12-30", "除夕");
        lFestival.put("1-1", "春节");
        lFestival.put("1-15", "元宵节");
        lFestival.put("2-2", "龙抬头");
        lFestival.put("5-5", "端午节");
        lFestival.put("7-7", "七夕节");
        lFestival.put("7-15", "中元节");
        lFestival.put("8-15", "中秋节");
        lFestival.put("9-9", "重阳节");
        lFestival.put("10-1", "寒衣节");
        lFestival.put("10-15", "下元节");
        lFestival.put("12-8", "腊八节");
        lFestival.put("12-23", "北方小年");
        lFestival.put("12-24", "南方小年");
    }

    /**
     * 24节气速查表
     *
     * @Array Of Property
     * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
     * @return Cn string
     */
    private static String[] solarTerm = {"\u5c0f\u5bd2", "\u5927\u5bd2", "\u7acb\u6625", "\u96e8\u6c34", "\u60ca\u86f0", "\u6625\u5206", "\u6e05\u660e", "\u8c37\u96e8", "\u7acb\u590f", "\u5c0f\u6ee1", "\u8292\u79cd", "\u590f\u81f3", "\u5c0f\u6691", "\u5927\u6691", "\u7acb\u79cb", "\u5904\u6691", "\u767d\u9732", "\u79cb\u5206", "\u5bd2\u9732", "\u971c\u964d", "\u7acb\u51ac", "\u5c0f\u96ea", "\u5927\u96ea", "\u51ac\u81f3"};

    /**
     * 1900-2100各年的24节气日期速查表
     *
     * @Array Of Property
     * @return 0x string For splice
     */

    private static String[] sTermInfo = {"9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c3598082c95f8c965cc920f", "97bd0b06bdb0722c965ce1cfcc920f", "b027097bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c359801ec95f8c965cc920f", "97bd0b06bdb0722c965ce1cfcc920f", "b027097bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c359801ec95f8c965cc920f", "97bd0b06bdb0722c965ce1cfcc920f", "b027097bd097c36b0b6fc9274c91aa", "9778397bd19801ec9210c965cc920e", "97b6b97bd19801ec95f8c965cc920f", "97bd09801d98082c95f8e1cfcc920f", "97bd097bd097c36b0b6fc9210c8dc2", "9778397bd197c36c9210c9274c91aa", "97b6b97bd19801ec95f8c965cc920e", "97bd09801d98082c95f8e1cfcc920f", "97bd097bd097c36b0b6fc9210c8dc2", "9778397bd097c36c9210c9274c91aa", "97b6b97bd19801ec95f8c965cc920e", "97bcf97c3598082c95f8e1cfcc920f", "97bd097bd097c36b0b6fc9210c8dc2", "9778397bd097c36c9210c9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c3598082c95f8c965cc920f", "97bd097bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c3598082c95f8c965cc920f", "97bd097bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c359801ec95f8c965cc920f", "97bd097bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c359801ec95f8c965cc920f", "97bd097bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf97c359801ec95f8c965cc920f", "97bd097bd07f595b0b6fc920fb0722", "9778397bd097c36b0b6fc9210c8dc2", "9778397bd19801ec9210c9274c920e", "97b6b97bd19801ec95f8c965cc920f", "97bd07f5307f595b0b0bc920fb0722", "7f0e397bd097c36b0b6fc9210c8dc2", "9778397bd097c36c9210c9274c920e", "97b6b97bd19801ec95f8c965cc920f", "97bd07f5307f595b0b0bc920fb0722", "7f0e397bd097c36b0b6fc9210c8dc2", "9778397bd097c36c9210c9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bd07f1487f595b0b0bc920fb0722", "7f0e397bd097c36b0b6fc9210c8dc2", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf7f1487f595b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf7f1487f595b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf7f1487f531b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c965cc920e", "97bcf7f1487f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b97bd19801ec9210c9274c920e", "97bcf7f0e47f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "9778397bd097c36b0b6fc9210c91aa", "97b6b97bd197c36c9210c9274c920e", "97bcf7f0e47f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "9778397bd097c36b0b6fc9210c8dc2", "9778397bd097c36c9210c9274c920e", "97b6b7f0e47f531b0723b0b6fb0722", "7f0e37f5307f595b0b0bc920fb0722", "7f0e397bd097c36b0b6fc9210c8dc2", "9778397bd097c36b0b70c9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721", "7f0e37f1487f595b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc9210c8dc2", "9778397bd097c36b0b6fc9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721", "7f0e27f1487f595b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "9778397bd097c36b0b6fc9274c91aa", "97b6b7f0e47f531b0723b0787b0721", "7f0e27f0e47f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "9778397bd097c36b0b6fc9210c91aa", "97b6b7f0e47f149b0723b0787b0721", "7f0e27f0e47f531b0723b0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "9778397bd097c36b0b6fc9210c8dc2", "977837f0e37f149b0723b0787b0721", "7f07e7f0e47f531b0723b0b6fb0722", "7f0e37f5307f595b0b0bc920fb0722", "7f0e397bd097c35b0b6fc9210c8dc2", "977837f0e37f14998082b0787b0721", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e37f1487f595b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc9210c8dc2", "977837f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "977837f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd097c35b0b6fc920fb0722", "977837f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "977837f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "977837f0e37f14998082b0787b06bd", "7f07e7f0e47f149b0723b0787b0721", "7f0e27f0e47f531b0b0bb0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "977837f0e37f14998082b0723b06bd", "7f07e7f0e37f149b0723b0787b0721", "7f0e27f0e47f531b0723b0b6fb0722", "7f0e397bd07f595b0b0bc920fb0722", "977837f0e37f14898082b0723b02d5", "7ec967f0e37f14998082b0787b0721", "7f07e7f0e47f531b0723b0b6fb0722", "7f0e37f1487f595b0b0bb0b6fb0722", "7f0e37f0e37f14898082b0723b02d5", "7ec967f0e37f14998082b0787b0721", "7f07e7f0e47f531b0723b0b6fb0722", "7f0e37f1487f531b0b0bb0b6fb0722", "7f0e37f0e37f14898082b0723b02d5", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e37f1487f531b0b0bb0b6fb0722", "7f0e37f0e37f14898082b072297c35", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e37f0e37f14898082b072297c35", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e37f0e366aa89801eb072297c35", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f149b0723b0787b0721", "7f0e27f1487f531b0b0bb0b6fb0722", "7f0e37f0e366aa89801eb072297c35", "7ec967f0e37f14998082b0723b06bd", "7f07e7f0e47f149b0723b0787b0721", "7f0e27f0e47f531b0723b0b6fb0722", "7f0e37f0e366aa89801eb072297c35", "7ec967f0e37f14998082b0723b06bd", "7f07e7f0e37f14998083b0787b0721", "7f0e27f0e47f531b0723b0b6fb0722", "7f0e37f0e366aa89801eb072297c35", "7ec967f0e37f14898082b0723b02d5", "7f07e7f0e37f14998082b0787b0721", "7f07e7f0e47f531b0723b0b6fb0722", "7f0e36665b66aa89801e9808297c35", "665f67f0e37f14898082b0723b02d5", "7ec967f0e37f14998082b0787b0721", "7f07e7f0e47f531b0723b0b6fb0722", "7f0e36665b66a449801e9808297c35", "665f67f0e37f14898082b0723b02d5", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e36665b66a449801e9808297c35", "665f67f0e37f14898082b072297c35", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e26665b66a449801e9808297c35", "665f67f0e37f1489801eb072297c35", "7ec967f0e37f14998082b0787b06bd", "7f07e7f0e47f531b0723b0b6fb0721", "7f0e27f1487f531b0b0bb0b6fb0722"};

    /**
     * 数字转中文速查表
     *
     * @Array Of Property
     * @trans ['日','一','二','三','四','五','六','七','八','九','十']
     * @return Cn string
     */

//    private static final String[] nStr1 = {"\u65e5", "\u4e00", "\u4e8c", "\u4e09", "\u56db", "\u4e94", "\u516d", "\u4e03", "\u516b", "\u4e5d", "\u5341"};
    // Chinese characters for numbers (ones)
    private static final String[] nStr1 = {"", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"};

    /**
     * 日期转农历称呼速查表
     *
     * @Array Of Property
     * @trans ['初','十','廿','卅']
     * @return Cn string
     */
//    private static final String[] nStr2 = {"\u521d", "\u5341", "\u5eff", "\u5345"};
    // Chinese characters for numbers (tens)
    private static final String[] nStr2 = {"", "初", "十", "廿", "卅"};


    /**
     * 月份转农历称呼速查表
     *
     * @Array Of Property
     * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
     * @return Cn string
     */
//    private static final String[] nStr3 = {"\u6b63", "\u4e8c", "\u4e09", "\u56db", "\u4e94", "\u516d", "\u4e03", "\u516b", "\u4e5d", "\u5341", "\u51ac", "\u814a"};
    private static String[] nStr3 = {"正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"};


    /**
     * 返回默认定义的阳历节日
     */
    public static Map<String, String> getFestival() {
        return festival;
    }

    /**
     * 按照festival的格式输入数据，设置农历节日
     *
     * @param festival
     */
    public static void setFestival(Map<String, String> festival) {
        LunarCalendarUtil.festival = festival;
    }

    /**
     * 返回默认定义的内容里节日
     */
    public static Map<String, String> getLunarFestival() {
        return lFestival;
    }

    /**
     * 按照lFestival的格式输入数据，设置农历节日
     *
     * @param lFestival
     */
    public static void setLunarFestival(Map<String, String> lFestival) {
        LunarCalendarUtil.lFestival = lFestival;
    }

    /**
     * 返回农历y年一整年的总天数
     *
     * @param y lunar Year
     * @return Number
     * @eg:var count = calendar.lYearDays(1987) ;//count=387
     */
    // 计算农历年份的总天数
//    public static int lYearDays(int y) {
//        int i, sum = 348;
//        for (i = 0x8000; i > 0x8; i >>= 1) {
//            sum += ((lunarInfo[y - 1900] & i) == 0 ? 1 : 0);
//        }
//        return (sum + leapDays(y));
//
//    }
    public static int lYearDays(int y) {
        int sum = 348; // 初始天数
        int info = lunarInfo[y - 1900]; // 获取对应年份的农历信息

        for (int i = 0x8000; i > 0x8; i >>= 1) {
            if ((info & i) != 0) {
                sum += 1; // 如果对应位为1，则增加一天
            }
        }

        return sum + leapDays(y); // 加上闰月的天数
    }
    /**
     * 返回农历y年闰月的天数 若该年没有闰月则返回0
     *
     * @param y lunar Year
     * @return Number (0、29、30)
     * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
     */
    // 计算农历年份闰月的天数
//    private static int leapDays(int y) {
//        if (leapMonth(y) != 0) {
//            return ((lunarInfo[y - 1900] & 0x10000) == 0 ? 29 : 30);
//        } else {
//            return 0;
//        }
//    }
    public static int leapDays(int y) {
        if (leapMonth(y) != 0) {
            return (lunarInfo[y - 1900] & 0x10000) != 0 ? 30 : 29;
        }
        return 0;
    }

    /**
     * 返回农历y年闰月是哪个月；若y年没有闰月 则返回0
     *
     * @param y lunar Year
     * @return Number (0-12)
     * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
     */
    // 计算农历年份闰哪个月
//    private static int leapMonth(int y) {
//        return (lunarInfo[y - 1900] & 0xf);
//    }
    public static int leapMonth(int y) {
        return lunarInfo[y - 1900] & 0xf;
    }
    /**
     * 返回农历y年m月（非闰月）的总天数，计算m为闰月时的天数请使用leapDays方法
     *
     * @param y lunar Year
     * @param m lunar Month
     * @return Number (-1、29、30)
     * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
     * //     * public static int monthDays(int y, int m) {
     * //     *         if (m > 12 || m < 1) {
     * //     *             return -1; // 月份参数从1至12，参数错误返回-1
     * //     *         }
     * //     *         return ((lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29);
     * //     *     }
     */

    // 计算农历月份的天数
//    public static int monthDays(int y, int m) {
//        if (m > 12 || m < 1) {
//            return -1; // 月份参数从1至12，参数错误返回-1
//        }
//        // 使用位运算来计算当月是否有闰月
//        int lunarYearInfo = lunarInfo[y - 1900];
//        int monthBitMask = 0x10000 >> m;
//        return (lunarYearInfo & monthBitMask) != 0 ? 30 : 29;
//    }
    public static int monthDays(int y, int m) {
        if (m > 12 || m < 1) {
            return -1; // 月份参数从1至12，参数错误返回-1
        }
        return (lunarInfo[y - 1900] & (0x10000 >> m)) != 0 ? 30 : 29;
    }

    /**
     * 返回公历(!)y年m月的天数
     *
     * @param y solar Year
     * @param m solar Month
     * @return Number (-1、28、29、30、31)
     * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
     */
//    public static int solarDays(int y, int m) {
//        if (m > 12 || m < 1) {
//            return -1; // 若参数错误 返回-1
//        }
//        int ms = m - 1; // 计算公历月份
//        if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
//            return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28);
//        } else {
//            return solarMonth[ms];
//        }
//    }
    /**
     * 返回公历(!)y年m月的天数
     *
     * @param y solar Year
     * @param m solar Month
     * @return Number (-1、28、29、30、31)
     */
    public static int solarDays(int y, int m) {
        if (m > 12 || m < 1) {
            return -1; // 若参数错误 返回-1
        }
        int ms = m - 1;
        if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
            return (isLeapYear(y) ? 29 : 28);
        } else {
            return solarMonth[ms];
        }
    }
    /**
     * 判断是否为闰年
     *
     * @param y 年份
     * @return 是否为闰年
     */
    public static boolean isLeapYear(int y) {
        return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
    }
    /**
     * 农历年份转换为干支纪年
     *
     * @param lYear 农历年的年份数
     * @return Cn string
     * //     *  toGanZhiYear: function (lYear) {
     * //     *     var ganKey = (lYear - 3) % 10;
     * //     *     var zhiKey = (lYear - 3) % 12;
     * //     *     if (ganKey === 0) ganKey = 10;//如果余数为0则为最后一个天干
     * //     *     if (zhiKey === 0) zhiKey = 12;//如果余数为0则为最后一个地支
     * //     *     return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1];
     * //     *
     * //     *   }
     */
//    public static String toGanZhiYear(int lYear) {
//        int ganKey = (lYear - 3) % 10;
//        int zhiKey = (lYear - 3) % 12;
//        if (ganKey == 0) {
//            ganKey = 10; // 如果余数为0则为最后一个天干
//        }
//        if (zhiKey == 0) {
//            zhiKey = 12; // 如果余数为0则为最后一个地支
//        }
//        return Gan[ganKey - 1] + Zhi[zhiKey - 1];
//    }
    public static String toGanZhiYear(int lYear) {
        int ganKey = (lYear - 3) % 10;
        int zhiKey = (lYear - 3) % 12;
        if (ganKey == 0) ganKey = 10; // 如果余数为0则为最后一个天干
        if (zhiKey == 0) zhiKey = 12; // 如果余数为0则为最后一个地支
        return Gan[ganKey - 1] + Zhi[zhiKey - 1];
    }
    /**
     * 公历月、日判断所属星座
     *
     * @param cMonth [description]
     * @param cDay [description]
     * @return Cn string
     * //     *  toAstro: function (cMonth, cDay) {
     * //     *     const s = "\u6469\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u6469\u7faf";
     * //     *     const arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22];
     * //     *     return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + "\u5ea7";//座
     * //     *   },
     */
    private static final String[] astroNames = {"\u6469\u7faf\u6c34\u74f6", "\u53cc\u9c7c", "\u767d\u7f8a", "\u91d1\u725b", "\u53cc\u5b50", "\u5de8\u87f9", "\u72ee\u5b50", "\u5904\u5973", "\u5929\u79e4", "\u5929\u874e", "\u5c04\u624b", "\u6469\u7faf"};

    public static String toAstro(int cMonth, int cDay) {
        if (cMonth < 1 || cMonth > 12 || cDay < 1 || cDay > 31) {
            return ""; // 参数错误，返回空字符串
        }
        int[] days = {20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22}; // 每个月的起始日期
        int index = cMonth - 1; // 月份索引
        String sign = ""; // 星座名称
        if (cDay < days[index]) {
            index--; // 如果日期小于当月的起始日期，则属于上个月的星座
        }
        sign = astroNames[index]; // 获取星座名称
        return sign;
    }


    /**
     * 传入offset偏移量返回干支
     *
     * @param offset 相对甲子的偏移量
     * @return Cn string
     */

//    public static String toGanZhi(int offset) {
//        return Gan[offset % 10] + Zhi[offset % 12];
//    }
    public static String toGanZhi(int offset) {
        return Gan[offset % 10] + Zhi[offset % 12];
    }
    /**
     * 传入公历(!)y年获得该年第n个节气的公历日期
     *
     * @param y y公历年(1900-2100)
     * @param n n二十四节气中的第几个节气(1~24)；从n=1(小寒)算起
     * @return day Number
     * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
     */
//    public static int getTerm(int y, int n) {
//        if (y < 1900 || y > 2100 || n < 1 || n > 24) {
//            return -1; // 参数错误，返回-1
//        }
//        String[] _calcDay = new String[sTermInfo.length]; // 用于存储节气的日期偏移量
//        for (int index = 0; index < sTermInfo.length; index += 5) {
//            _calcDay[index / 5] = sTermInfo[index] + sTermInfo[index + 1] + sTermInfo[index + 2] + sTermInfo[index + 3];
//        }
//        return Integer.parseInt(_calcDay[n - 1]);
//    }

//    public static int getTerm(int y, int n) {
//        if (y < 1900 || y > 2100 || n < 1 || n > 24) {
//            return -1;
//        }
//        String _table = sTermInfo[y - 1900];
//        StringBuilder _calcDay = new StringBuilder();
//        for (int index = 0; index < _table.length(); index += 5) {
//            String chunk = Integer.toString(Integer.parseInt(_table.substring(index, index + 5), 16));
//            _calcDay.append(chunk.charAt(0))
//                    .append(chunk.substring(1, 3))
//                    .append(chunk.charAt(3))
//                    .append(chunk.substring(4, 6));
//        }
//        return Integer.parseInt(_calcDay.substring((n - 1) * 2, (n - 1) * 2 + 2));
//    }
    public static int getTerm(int y, int n) {
        if (y < 1900 || y > 2100 || n < 1 || n > 24) {
            return -1;
        }

        String _table = sTermInfo[y - 1900];
        List<String> _calcDay = new ArrayList<>();

        for (int index = 0; index < _table.length(); index += 5) {
            String chunk = Integer.toString(Integer.parseInt(_table.substring(index, index + 5), 16));
            _calcDay.add(chunk.substring(0, 1));
            _calcDay.add(chunk.substring(1, 3));
            _calcDay.add(chunk.substring(3, 4));
            _calcDay.add(chunk.substring(4, 6));
        }

        return Integer.parseInt(_calcDay.get(n - 1));
    }
    /**
     * 传入农历数字月份返回汉语通俗表示法
     *
     * @param m lunar month
     * @return Cn string
     * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
     * //     * toChinaMonth: function (m) { // 月 => \u6708
     * //     *         if (m > 12 || m < 1) {
     * //     *             return -1
     * //     *         } //若参数错误 返回-1
     * //     *         let s = this.nStr3[m - 1];
     * //     *         s += "\u6708";//加上月字
     * //     *         return s;
     * //     *     },
     */
//    public static String toChinaMonth(int m) {
//        if (m > 12 || m < 1) {
//            return "-1"; // 若参数错误 返回-1
//        }
//        String s = nStr3[m - 1];
//        s += "\u6708"; // 加上月字
//        return s;
//    }

    public static String toChinaMonth(int m) {
        if (m > 12 || m < 1) {
            return "-1";  // If the parameter is incorrect, return -1 as a string
        }
        String s = nStr3[m - 1];
        s += "月"; // Append the Chinese character for "month"
        return s;
    }

    /**
     * 传入农历日期数字返回汉字表示法
     *
     * @param d lunar day
     * @return Cn string
     * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
     * //     * toChinaDay: function (d) { //日 => \u65e5
     * //     *         let s;
     * //     *         switch (d) {
     * //     *             case 10:
     * //     *                 s = '\u521d\u5341';
     * //     *                 break;
     * //     *             case 20:
     * //     *                 s = '\u4e8c\u5341';
     * //     *                 break;
     * //     *             case 30:
     * //     *                 s = '\u4e09\u5341';
     * //     *                 break;
     * //     *             default:
     * //     *                 s = this.nStr2[Math.floor(d / 10)];
     * //     *                 s += this.nStr1[d % 10];
     * //     *         }
     * //     *         return (s);
     * //     *     },
     */
//    public static String toChinaDay(int d) {
//        String s;
//        switch (d) {
//            case 10:
//                s = nStr2[0] + nStr1[0];
//                break;
//            case 20:
//                s = nStr2[1] + nStr1[1];
//                break;
//            case 30:
//                s = nStr2[2] + nStr1[2];
//                break;
//            default:
//                s = nStr2[(int) Math.floor(d / 10)] + nStr1[d % 10];
//                break;
//        }
//        return s;
//    }

    public static String toChinaDay(int d) {
        if (d < 1 || d > 30) {
            return null;  // If the day is out of range, return null
        }

        String s;
        switch (d) {
            case 10:
                s = "初十";
                break;
            case 20:
                s = "二十";
                break;
            case 30:
                s = "三十";
                break;
            default:
                s = nStr2[d / 10];  // Tens place
                s += nStr1[d % 10]; // Ones place
        }
        return s;
    }
    /**
     * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
     *
     * @param y year
     * @return Cn string
     * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
     * //     *     getAnimal: function (y) {
     * //     *         return this.Animals[(y - 4) % 12]
     * //     *     },
     */
    // 返回生肖
//    public static String getAnimal(int y) {
//        return Animals[(y - 4) % 12];
//    }

    public static String getAnimal(int y) {
        return Animals[(y - 4) % 12]; // Calculate the zodiac animal based on the year
    }
    /**
     * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
     * !important! 公历参数区间1900.1.31~2100.12.31
     * @param yPara  solar year
     * @param mPara  solar month
     * @param dPara  solar day
     * @return JSON object
     * @eg:console.log(calendar.solar2lunar(1987,11,01));
     */
//    solar2lunar: function (yPara, mPara, dPara) {
//        let y = parseInt(yPara);
//        let m = parseInt(mPara);
//        let d = parseInt(dPara);
//        //年份限定、上限
//        if (y < 1900 || y > 2100) {
//            return -1;// undefined转换为数字变为NaN
//        }
//        //公历传参最下限
//        if (y === 1900 && m === 1 && d < 31) {
//            return -1;
//        }
//
//        //未传参  获得当天
//        let objDate;
//        if (!y) {
//            objDate = new Date();
//        } else {
//            objDate = new Date(y, parseInt(m) - 1, d);
//        }
//        let i, leap = 0, temp = 0;
//        //修正ymd参数
//        y = objDate.getFullYear();
//        m = objDate.getMonth() + 1;
//        d = objDate.getDate();
//        let offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000;
//        for (i = 1900; i < 2101 && offset > 0; i++) {
//            temp = this.lYearDays(i);
//            offset -= temp;
//        }
//        if (offset < 0) {
//            offset += temp;
//            i--;
//        }
//
//        //是否今天
//        let isTodayObj = new Date(),
//                isToday = false;
//        if (isTodayObj.getFullYear() === y && isTodayObj.getMonth() + 1 === m && isTodayObj.getDate() === d) {
//            isToday = true;
//        }
//        //星期几
//        let nWeek = objDate.getDay(),
//                cWeek = this.nStr1[nWeek];
//        //数字表示周几顺应天朝周一开始的惯例
//        if (nWeek === 0) {
//            nWeek = 7;
//        }
//        //农历年
//    const year = i;
//        leap = this.leapMonth(i); //闰哪个月
//        let isLeap = false;
//
//        //效验闰月
//        for (i = 1; i < 13 && offset > 0; i++) {
//            //闰月
//            if (leap > 0 && i === (leap + 1) && isLeap === false) {
//                --i;
//                isLeap = true;
//                temp = this.leapDays(year); //计算农历闰月天数
//            } else {
//                temp = this.monthDays(year, i);//计算农历普通月天数
//            }
//            //解除闰月
//            if (isLeap === true && i === (leap + 1)) {
//                isLeap = false;
//            }
//            offset -= temp;
//        }
//        // 闰月导致数组下标重叠取反
//        if (offset === 0 && leap > 0 && i === leap + 1) {
//            if (isLeap) {
//                isLeap = false;
//            } else {
//                isLeap = true;
//                --i;
//            }
//        }
//        if (offset < 0) {
//            offset += temp;
//            --i;
//        }
//        //农历月
//    const month = i;
//        //农历日
//    const day = offset + 1;
//        //天干地支处理
//    const sm = m - 1;
//    const gzY = this.toGanZhiYear(year);
//
//        // 当月的两个节气
//        // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
//    const firstNode = this.getTerm(y, (m * 2 - 1));//返回当月「节」为几日开始
//    const secondNode = this.getTerm(y, (m * 2));//返回当月「节」为几日开始
//
//        // 依据12节气修正干支月
//        let gzM = this.toGanZhi((y - 1900) * 12 + m + 11);
//        if (d >= firstNode) {
//            gzM = this.toGanZhi((y - 1900) * 12 + m + 12);
//        }
//
//        //传入的日期的节气与否
//        let isTerm = false;
//        let Term = null;
//        if (firstNode === d) {
//            isTerm = true;
//            Term = this.solarTerm[m * 2 - 2];
//        }
//        if (secondNode === d) {
//            isTerm = true;
//            Term = this.solarTerm[m * 2 - 1];
//        }
//        //日柱 当月一日与 1900/1/1 相差天数
//    const dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10;
//    const gzD = this.toGanZhi(dayCyclical + d - 1);
//        //该日期所属的星座
//    const astro = this.toAstro(m, d);
//
//    const solarDate = y + '-' + m + '-' + d;
//    const lunarDate = year + '-' + month + '-' + day;
//
//    const festival = this.festival;
//    const lFestival = this.lFestival;
//
//    const festivalDate = m + '-' + d;
//        let lunarFestivalDate = month + '-' + day;
//
//        // bugfix https://github.com/jjonline/calendar.js/issues/29
//        // 农历节日修正：农历12月小月则29号除夕，大月则30号除夕
//        // 此处取巧修正：当前为农历12月29号时增加一次判断并且把lunarFestivalDate设置为12-30以正确取得除夕
//        // 天朝农历节日遇闰月过前不过后的原则，此处取农历12月天数不考虑闰月
//        // 农历润12月在本工具支持的200年区间内仅1574年出现
//        if (month === 12 && day === 29 && this.monthDays(year, month) === 29) {
//            lunarFestivalDate = '12-30';
//        }
//        return {
//                date: solarDate,
//                lunarDate: lunarDate,
//                festival: festival[festivalDate] ? festival[festivalDate].title : null,
//                lunarFestival: lFestival[lunarFestivalDate] ? lFestival[lunarFestivalDate].title : null,
//                'lYear': year,
//                'lMonth': month,
//                'lDay': day,
//                'Animal': this.getAnimal(year),
//                'IMonthCn': (isLeap ? "\u95f0" : '') + this.toChinaMonth(month),
//                'IDayCn': this.toChinaDay(day),
//                'cYear': y,
//                'cMonth': m,
//                'cDay': d,
//                'gzYear': gzY,
//                'gzMonth': gzM,
//                'gzDay': gzD,
//                'isToday': isToday,
//                'isLeap': isLeap,
//                'nWeek': nWeek,
//                'ncWeek': "\u661f\u671f" + cWeek,
//                'isTerm': isTerm,
//                'Term': Term,
//                'astro': astro
//    };
//    },

    /**
     * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
     * !important! 公历参数区间1900.1.31~2100.12.31
     *
     * @param yPara solar year
     * @param mPara solar month
     * @param dPara solar day
     * @return JSON object
     */
    public static Map<String, Object> solar2lunar(int yPara, int mPara, int dPara) {
        int y = yPara;
        int m = mPara;
        int d = dPara;

        if (y < 1900 || y > 2100) {
            return null;
        }
        if (y == 1900 && m == 1 && d < 31) {
            return null;
        }

        Calendar objDate = Calendar.getInstance();
        if (y == 0) {
            objDate.setTime(new Date());
        } else {
            objDate.set(y, m - 1, d);
        }

        y = objDate.get(Calendar.YEAR);
        m = objDate.get(Calendar.MONTH) + 1;
        d = objDate.get(Calendar.DAY_OF_MONTH);

        long offset = (objDate.getTimeInMillis() - getUTC(1900, 1, 31)) / 86400000;

        int temp = 0, leap = 0;
        int i;
        for (i = 1900; i < 2101 && offset > 0; i++) {
            temp = lYearDays(i);
            offset -= temp;
        }
        if (offset < 0) {
            offset += temp;
            i--;
        }

        boolean isToday = isToday(y, m, d);
        int nWeek = objDate.get(Calendar.DAY_OF_WEEK) - 1;
        String cWeek = nStr1[nWeek];
        if (nWeek == 0) {
            nWeek = 7;
        }

        int year = i;
        leap = leapMonth(i);
        boolean isLeap = false;

        for (i = 1; i < 13 && offset > 0; i++) {
            if (leap > 0 && i == (leap + 1) && !isLeap) {
                i--;
                isLeap = true;
                temp = leapDays(year);
            } else {
                temp = monthDays(year, i);
            }
            if (isLeap && i == (leap + 1)) {
                isLeap = false;
            }
            offset -= temp;
        }
        if (offset == 0 && leap > 0 && i == leap + 1) {
            if (isLeap) {
                isLeap = false;
            } else {
                isLeap = true;
                i--;
            }
        }
        if (offset < 0) {
            offset += temp;
            i--;
        }

        int month = i;
        int day = (int) (offset + 1);
        String gzY = toGanZhiYear(year);
        int firstNode = getTerm(y, m * 2 - 1);
        int secondNode = getTerm(y, m * 2);
        String gzM = toGanZhi((y - 1900) * 12 + m + 11);
        if (d >= firstNode) {
            gzM = toGanZhi((y - 1900) * 12 + m + 12);
        }

        boolean isTerm = false;
        String term = null;
        if (firstNode == d) {
            isTerm = true;
            term = solarTerm[m * 2 - 2];
        }
        if (secondNode == d) {
            isTerm = true;
            term = solarTerm[m * 2 - 1];
        }

        long dayCyclical = getUTC(y, m - 1, 1) / 86400000 + 25567 + 10;
        String gzD = toGanZhiYear((int) (dayCyclical + d - 1));

        String astro = toAstro(m, d);

        String solarDate = y + "-" + m + "-" + d;
        String lunarDate = year + "-" + month + "-" + day;

        String festivalDate = m + "-" + d;
        String lunarFestivalDate = month + "-" + day;

        if (month == 12 && day == 29 && monthDays(year, month) == 29) {
            lunarFestivalDate = "12-30";
        }

        Map<String, Object> result = new HashMap<>();
        result.put("date", solarDate);
        result.put("lunarDate", lunarDate);
        result.put("festival", festival.get(festivalDate));
        result.put("lunarFestival", lFestival.get(lunarFestivalDate));
        result.put("lYear", year);
        result.put("lMonth", month);
        result.put("lDay", day);
        result.put("Animal", getAnimal(year));
        result.put("IMonthCn", (isLeap ? "闰" : "") + toChinaMonth(month));
        result.put("IDayCn", toChinaDay(day));
        result.put("cYear", y);
        result.put("cMonth", m);
        result.put("cDay", d);
        result.put("gzYear", gzY);
        result.put("gzMonth", gzM);
        result.put("gzDay", gzD);
        result.put("isToday", isToday);
        result.put("isLeap", isLeap);
        result.put("nWeek", nWeek);
        result.put("ncWeek", "星期" + cWeek);
        result.put("isTerm", isTerm);
        result.put("Term", term);
        result.put("astro", astro);

        return result;
    }

    private static long getUTC(int y, int m, int d) {
        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        calendar.set(y, m - 1, d, 0, 0, 0);
        return calendar.getTimeInMillis();
    }

    private static boolean isToday(int y, int m, int d) {
        Calendar today = Calendar.getInstance();
        return (today.get(Calendar.YEAR) == y && (today.get(Calendar.MONTH) + 1) == m && today.get(Calendar.DAY_OF_MONTH) == d);
    }

    /**
     * 传入农历年月日获得详细的公历、农历object信息 <=>JSON
     * !important! 公历参数区间1900.1.31~2100.12.31
     *
     * @param y           lunar year
     * @param m           lunar month
     * @param d           lunar day
     * @param isLeapMonth 是否闰月
     * @return JSON object
     */
    public static Map<String, Object> lunar2solar(int y, int m, int d, boolean isLeapMonth) {
        int leapMonth = leapMonth(y);

        if (isLeapMonth && (leapMonth != m)) {
            return null; // 如果是闰月，但月份与闰月不匹配，返回null
        }
        if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) {
            return null; // 超出了合法范围
        }

        int day = monthDays(y, m);
        int _day = day;

        if (isLeapMonth) {
            _day = leapDays(y); // 如果是闰月，使用闰月天数
        }
        if (y < 1900 || y > 2100 || d > _day) {
            return null; // 参数合法性验证
        }

        // 计算农历的时间差
        int offset = 0;
        for (int i = 1900; i < y; i++) {
            offset += lYearDays(i);
        }

        boolean isAdd = false;
        for (int i = 1; i < m; i++) {
            leapMonth = leapMonth(y);
            if (!isAdd && leapMonth > 0 && leapMonth <= i) {
                offset += leapDays(y);
                isAdd = true;
            }
            offset += monthDays(y, i);
        }

        if (isLeapMonth) {
            offset += day; // 如果是闰月，加上前一个月的天数
        }

        long strap = getUTC(1900, 1, 30);
        long calTime = (offset + d - 31) * 86400000L + strap;
        Calendar calObj = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        calObj.setTimeInMillis(calTime);

        int cY = calObj.get(Calendar.YEAR);
        int cM = calObj.get(Calendar.MONTH) + 1;
        int cD = calObj.get(Calendar.DAY_OF_MONTH);

        return solar2lunar(cY, cM, cD);
    }


    public static void main(String[] args) {
        System.out.println(LunarCalendarUtil.solar2lunar(2024, 9, 7));
        System.out.println(LunarCalendarUtil.lunar2solar(1987, 9, 10,false));




    }
}
